﻿namespace AgileDashboard.AppCode
{
    using System;
    using System.Diagnostics;
    using System.Web;
    using System.Web.Caching;
    using System.Xml.XPath;

    /// <summary>
    /// DAO for fetching and caching Iteration objects
    /// </summary>
    public class IterationDao : DaoBase
    {
        public IterationDao(IDataConnection dataConnection) : base(dataConnection)
        {
        }


        /// <summary>
        /// Gets the current Iteration from Version One and adds the object to the cache. The object is cached until the end date of the iteration.
        /// </summary>
        /// <param name="iterationSchedule">Find the current iteration belonging to the specified schedule</param>
        /// <param name="bypassCache">Ignore the cached version and requery Version One for the Iteration</param>
        /// <returns>An Iteration object for the specified schedule</returns>
        public Iteration GetCurrentIterationBySchedule(string iterationSchedule, bool bypassCache)
        {
            string cacheKey = iterationSchedule + "_CurrentIteration";

            var iteration = new Iteration();

            var thisLock = new object();
            lock (thisLock)
            {
                var iterationName = HttpRuntime.Cache[cacheKey] as string;

                if (bypassCache || (iterationName == null))
                {
                    Debug.WriteLine(string.Format("Cache miss for key {0}", cacheKey));
                    if (Log.IsDebugEnabled)
                    {
                        Log.DebugFormat("Cache miss for key {0}", cacheKey);
                    }

                    iteration = this.LoadCurrentIterationBySchedule(iterationSchedule);

                    if (iterationName != null)
                    {
                        HttpRuntime.Cache.Insert(
                            cacheKey,
                            iterationName,
                            null,
                            iteration.EndDate,
                            Cache.NoSlidingExpiration,
                            this.GetCurrentIterationByScheduleCacheUpdateCallback);
                    }
                }
                else
                {
                    Debug.WriteLine(string.Format("Cache hit for key {0}", cacheKey));
                    if (Log.IsDebugEnabled)
                    {
                        Log.DebugFormat("Cache hit for key {0}", cacheKey);
                    }
                }

                return iteration;
            }
        }

        private Iteration LoadCurrentIterationBySchedule(string iterationSchedule)
        {
            var iteration = new Iteration();
            if (iterationSchedule == null)
            {
                throw new ArgumentNullException("iterationSchedule");
            }

            var query = string.Format("Timebox?sel=Name,EndDate,BeginDate,Schedule.Name&where=Schedule.Name='{0}';BeginDate<='{1}';EndDate>='{1}'", Microsoft.Security.Application.Encoder.UrlEncode(iterationSchedule), Microsoft.Security.Application.Encoder.UrlEncode(DateTime.Now.Date.ToShortDateString()));

            var ret = DataConnection.Execute(query);

            if (ret != null && ret.Document != null)
            {
                var assetElem = ret.XPathSelectElement("/Assets/Asset");
                iteration.BeginDate = Util.GetValue<DateTime>("BeginDate", assetElem);
                iteration.EndDate = Util.GetValue<DateTime>("EndDate", assetElem);
                iteration.Name = Util.GetValue<string>("Name", assetElem);
                iteration.Schedule = Util.GetValue<string>("Schedule.Name", assetElem);
            }
            else
            {
                Log.Error("No response from server");
            }

            return iteration;
        }

        // caching callback method
        private void GetCurrentIterationByScheduleCacheUpdateCallback(string key, CacheItemUpdateReason reason, out object expensiveObject, out CacheDependency dependency, out DateTime absoluteExpiration, out TimeSpan slidingExpiration)
        {
            absoluteExpiration = Cache.NoAbsoluteExpiration;
            slidingExpiration = Cache.NoSlidingExpiration;
            dependency = null;
            expensiveObject = null;

            var iteration = HttpRuntime.Cache[key] as Iteration;

            if (iteration == null)
            {
                // for some reason, there isn't a valid iteration object in the cache. Not good. 
                // If this is the case, the cache manager will remove the cached object when this callback returns
                return;
            }

            // if there is a valid cached iteration object, replace it with a fresh object
            iteration = this.LoadCurrentIterationBySchedule(iteration.Schedule);

            if (iteration == null)
            {
                return;
            }

            expensiveObject = iteration;
            absoluteExpiration = iteration.EndDate;
            
            Debug.WriteLine(string.Format("Refreshed cache for key {0}", key));
            if (Log.IsDebugEnabled)
            {
                Log.DebugFormat("Refreshed cache for key {0}", key);
            }
        }

    }
}